package com.venusource.wechat.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.venusource.conf.WechatProperties;
import com.venusource.consts.JsApiTicketEnum;
import com.venusource.exception.BusinessException;
import com.venusource.utils.JwtUtil;
import com.venusource.wechat.service.IWechatService;
import com.venusource.wechat.vo.*;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

@Service
@Slf4j
public class WechatServiceImpl implements IWechatService {

  @Autowired
  private RestTemplate restTemplate;

  @Autowired
  private WechatProperties wechatProperties;

  @Autowired
  private ObjectMapper objectMapper;

  @Override
  public AccessToken getAccessToken() {
    return getAccessTokenByUrl(wechatProperties.getAccessTokenUrl());
  }

  @Override
  public AccessToken getAddressBookAccessToken() {
    return getAccessTokenByUrl(wechatProperties.getAddressBookAccessTokenUrl());
  }

  private AccessToken getAccessTokenByUrl(String url) {
    AccessToken accessToken = restTemplate.getForObject(url, AccessToken.class);
    if (log.isDebugEnabled()) {
      log.debug(accessToken.toString());
    }
    return accessToken;
  }

  @Override
  public JsApiTicket getJsApiTicket() {
    return getJsApiTicketByUrl(wechatProperties.getJsApiTicketUrl());
  }

  @Override
  public JsApiTicket getAgentJsApiTicket() {
    return getJsApiTicketByUrl(wechatProperties.getAgentJsApiTicketUrl());
  }

  private JsApiTicket getJsApiTicketByUrl(String url) {
    AccessToken accessToken = getAccessToken();
    url = url.replace("{accessToken}", accessToken.getAccess_token());
    return restTemplate.getForObject(url, JsApiTicket.class);
  }

  @Override
  public String redirect(String url, String state) {
    return wechatProperties.getOauth2Url().replace("{redirect_uri}", url).replace("{state}", state);
  }

  @Override
  public String authorize(String code) throws BusinessException {
    AccessToken accessToken = getAccessToken();
    String url = wechatProperties.getUserByCodeUrl().replace("{accessToken}", accessToken.getAccess_token()).replace("{code}", code);
    Map map = restTemplate.getForObject(url, Map.class);
    String userId = map.get("UserId").toString();
    //avatar_addr=1获取到的头像链接为外网链接，avatar_addr=0获取到的头像链接为内网链接。用户在外网访问内网应用的场景下内网应用需要判断网络性质从而采用核实的头像地址获取方案。如果不设置该参数，获取到的头像需要进行链接替换。
    String urlDetailUrl = wechatProperties.getUserDetailUrl().replace("{accessToken}", accessToken.getAccess_token()).replace("{userId}", userId).replace("{avatar_addr}", "1");
    UserDetail userDetail = restTemplate.getForObject(urlDetailUrl, UserDetail.class);
    try {
      return JwtUtil.createJWT(userDetail.getUserid(), objectMapper.writeValueAsString(userDetail));
    } catch (JsonProcessingException e) {
      log.error("生成token失败");
      throw new BusinessException("生成token失败");
    }
  }

  public Optional<UserDetail> parserToken() {
    Claims claims = null;
    UserDetail userDetail = null;
    try {
      claims = JwtUtil.parseJWT(getToken());
      userDetail = objectMapper.readValue(claims.getSubject(), UserDetail.class);
    } catch (Exception e) {
      log.error("token解析用户失败：", e);
    }
    return Optional.of(userDetail);
  }

  private HttpServletRequest getHttpServletRequest() {
    return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  }

  private String getToken() {
    HttpServletRequest request = getHttpServletRequest();
    String token = request.getHeader("token");
    if (token == null) {
      token = request.getParameter("token");
    }
    return token;
  }

  @Override
  public Config getConfig(String url) {
    JsApiTicket jsApiTicket = getJsApiTicket();
    Config config = new Config();
    long timestamp = System.currentTimeMillis();
    String noncestr = randomString();
    config.setAppId(wechatProperties.getCorpId());
    config.setNonceStr(noncestr);
    config.setSignature(signature(jsApiTicket.getTicket(), noncestr, timestamp, url));
    config.setTimestamp(timestamp);
    return config;
  }

  @Override
  public AgentConfig getAgentConfig(String url) {
    JsApiTicket jsApiTicket = getAgentJsApiTicket();
    AgentConfig config = new AgentConfig();

    String noncestr = randomString();
    long timestamp = System.currentTimeMillis();

    config.setCorpid(wechatProperties.getCorpId());
    config.setAgentid(wechatProperties.getAgentid().toString());
    config.setNonceStr(noncestr);
    config.setSignature(signature(jsApiTicket.getTicket(), noncestr, timestamp, url));
    config.setTimestamp(timestamp);

    if (log.isDebugEnabled()) {
      log.debug(config.toString());
    }
    return config;
  }

  private String randomString() {
    return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
  }

  private String signature(String jsApiTicket, String noncestr, Long timestamp, String url) {
    String temp = String.format("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s", jsApiTicket, noncestr, timestamp, url);
    String signature = null;
    try {
      signature = DigestUtils.sha1Hex(temp.getBytes("UTF-8"));
    } catch (UnsupportedEncodingException e) {
      log.error("签名失败：", e);
    }
    return signature;
  }

  @Override
  public void send(TextMessage textMessage) {
    AccessToken accessToken = getAccessToken();
    String url = wechatProperties.getMessageUrl().replace("{accessToken}", accessToken.getAccess_token());

    textMessage.setMsgtype("text");
    textMessage.setAgentid(wechatProperties.getAgentid());
    textMessage.setTotag("");
    if (textMessage.getToparty() == null){
      textMessage.setToparty("");
    }
    HttpEntity entity = new HttpEntity(textMessage);
    ResponseEntity<MessageResult> response = restTemplate.postForEntity(url, entity, MessageResult.class);
    log.error("消息发送结果:{}", response.getBody());
  }
}
